using System;
using System.Text;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;

namespace Sio.Mdm.Utils
{
	/// <summary>
	/// This class can be used to execute dynamic uncompiled code at runtime
	/// This class is not exception safe, all function calls should be exception handled.
	/// </summary>
	public static class CodeCompiler
	{
		/// <summary>
		/// Default Constructor.
		/// It is necessary to have an instance of the class so that the reflection
		/// can get the namespace of this class dynamically.
		/// </summary>
		/// <remarks>
		/// This class could be static, but I wanted to make it easy for developers
		/// to use this class by allowing them to change the namespace without
		/// harming the code.
		/// </remarks>
		static CodeCompiler()
		{
		}

        /// <summary>
        /// Create interface for passed type. Currently not much is known of origin
        /// or intention of this function. It's just doing job assigned ;)
        /// </summary>
        /// <param name="type">Type to generate interface for</param>
        /// <returns>Generated interface type</returns>
        public static Type CreateInterfaceForType(Type type)
        {
            StringBuilder builder = new StringBuilder();

            builder.Append("using System;");
            builder.AppendFormat("namespace {0}{{", type.Namespace);
            builder.AppendFormat("public interface I{0}{{", type.Name);

            foreach (PropertyInfo prop in type.GetProperties())
            {
                if (prop.Name == "Error")
                {
                    continue;
                }

                builder.AppendFormat("{0} {1}{{", prop.PropertyType.Name, prop.Name);

                if (prop.CanRead)
                {
                    builder.Append("get;");
                }

                if (prop.CanWrite)
                {
                    builder.Append("set;");
                }

                builder.Append("}");
            }

            builder.Append("}}");

            return BuildAssembly(builder.ToString()).GetTypes()[0];
        }
		
		/// <summary>
		/// This private function builds the assembly file into memory after compiling the code
		/// </summary>
		/// <param name="code">C# code to be compiled</param>
		/// <returns>the compiled assembly object</returns>
		private static Assembly BuildAssembly(string code)
		{
#if DEBUG
			Microsoft.CSharp.CSharpCodeProvider provider = new CSharpCodeProvider();
			ICodeCompiler compiler = provider.CreateCompiler();
			CompilerParameters compilerparams = new CompilerParameters();
			compilerparams.GenerateExecutable = false;
			compilerparams.GenerateInMemory = true;
			CompilerResults results = compiler.CompileAssemblyFromSource(compilerparams, code);
			
            if (results.Errors.HasErrors)
			{
				StringBuilder errors = new StringBuilder("Compiler Errors :\r\n");
				foreach (CompilerError error in results.Errors )
				{
					errors.AppendFormat("Line {0},{1}\t: {2}\n", error.Line, error.Column, error.ErrorText);
				}
				throw new Exception(errors.ToString());
			}
			else
			{
				return results.CompiledAssembly;
			}
#else
            return null;
#endif
		}
	}
}
